//
//  MFAPMCrashMonitor.m
//  MFAPMManager
//
//  Created by zhang liyu on 2019/5/26.
//

#import "MFAPMCrashMonitor.h"

#import "MFAPMCrashMonitorModel.h"
#import "MFAPMZombineSniffer.h"
#import "MFAPMUploadManager.h"
#import "MFAPMUtility.h"
#import "AvoidCrash.h"

#include <libkern/OSAtomic.h>
#include <execinfo.h>

void InstallSignalExceptionHandler(void);
void InstallUncaughtExceptionHandler(void);

@implementation MFAPMCrashMonitor

#pragma mark - Singleton

+ (instancetype)sharedInstance{
    static MFAPMCrashMonitor *crashMonitor = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        crashMonitor = [[MFAPMCrashMonitor alloc] init];
    });
    return crashMonitor;
}

#pragma mark - Life Circle

- (instancetype)init{
    self = [super init];
    if(self){
        [self configActions];
    }
    return self;
}

- (void)configActions{
}

#pragma mark - Monitor Control Actions

- (void)startMonitor{
    InstallUncaughtExceptionHandler();
    InstallSignalExceptionHandler();
    [self startAvoidCrash];
    //因添加后会造成网络监听请求超时，暂时注释，后期排查原因并优化
//    [self startZombineSniffer];
}

- (void)stopMonitor{
    [self stopAvoidCrash];
    //暂时注释，待后续打开时进行对应处理
//    [self stopZombineSniffer];
}

#pragma mark - Avoid Crash Actions

- (void)startAvoidCrash{
    [AvoidCrash makeAllEffective];
    
    //生成监听unrecognizeSelector方法的类名数组，用于监听找不到方法的崩溃
    NSArray *noneSelClassStringsArr = @[
                                        @"NSNull",
                                        @"NSNumber",
                                        @"NSString",
                                        @"NSMutableString",
                                        @"NSDictionary",
                                        @"NSMutableDictionary",
                                        @"NSArray",
                                        @"NSMutableArray",
                                        @"NSSet",
                                        @"NSMutableSet",
                                        ];
    [AvoidCrash setupNoneSelClassStringsArr:noneSelClassStringsArr];
    //暂时注释，因为会偶现将原来存在的方法判断为不存在对应实现方法，并将其替换为默认方法执行，会偶现参数不匹配的崩溃，具体原因需要后续进行确认下
//    //生成监听含有对应前缀的类的unrecognizeSelector方法的类名数组，用于监听找不到方法的崩溃
//    NSArray *noneSelClassPrefixArr = @[
//                                    @"MF",
//                                    @"MFAPM"
//                                    ];
//    [AvoidCrash setupNoneSelClassStringPrefixsArr:noneSelClassPrefixArr];
    
    
    
    //监听通知:AvoidCrashNotification, 获取AvoidCrash捕获的崩溃日志的详细信息
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAvoidCrashMessage:) name:AvoidCrashNotification object:nil];
}

- (void)stopAvoidCrash{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AvoidCrashNotification object:nil];
}

- (void)handleAvoidCrashMessage:(NSNotification *)notification{
 /**
  exceptionInfoDict 字段:
                         @"errorName"
                         @"errorReason"
                         @"errorPlace"
                         @"defaultToDo"
                         @"callStackSymbols"
                         @"exception"
  */
    NSDictionary *exceptionInfoDict = notification.userInfo;
    if(![exceptionInfoDict isKindOfClass:[NSDictionary class]]
       ||exceptionInfoDict.count == 0){
        return;
    }

    MFAPMCrashMonitorModel *exceptionModel = [[MFAPMCrashMonitorModel alloc] init];
    
    //错误信息
    NSString *exceptionDescription = [NSString stringWithFormat:@"errorName:%@\n errorReason:%@\n errorPlace:%@\n defaultToDo:%@\n exception:%@\n", exceptionInfoDict[@"errorName"] ? : @"", exceptionInfoDict[@"errorReason"] ? : @"", exceptionInfoDict[@"errorPlace"] ? : @"", exceptionInfoDict[@"defaultToDo"] ? : @"", exceptionInfoDict[@"exception"] ? : @""];
    exceptionModel.exception = exceptionDescription;
    
    //信号量
    exceptionModel.isSignal = NO;
    
    //堆栈
    NSArray *stackArray = exceptionInfoDict[@"callStackSymbols"];
    if([stackArray isKindOfClass:[NSArray class]] && stackArray.count > 0){
        exceptionModel.stack = [stackArray componentsJoinedByString:@"\n"] ? : @"";
    }
    
    //扩展信息
    [exceptionModel configExtend];
    
    //因程序此时并不崩溃，故保存崩溃信息并等待上传
    [MFAPMUploadManager cacheItem:exceptionModel];
}

#pragma mark - Zombine Sniffer Actions

- (void)startZombineSniffer{
    [MFAPMZombineSniffer installSniffer];
    
    //添加zombine监听白名单，添加的类不进行zombine的监听处理
//    [MFAPMZombineSniffer appendIgnoreClass:nil];
    
    //添加监听，进行zombine对象被调用时的消息处理
    [[NSNotificationCenter defaultCenter]  addObserver:self selector:@selector(handleZombineSnifferActions:) name:kMFAPMZombineSnifferCallNotification object:nil];
}

- (void)stopZombineSniffer{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kMFAPMZombineSnifferCallNotification object:nil];
    [MFAPMZombineSniffer uninstallSnifier];
}

- (void)handleZombineSnifferActions:(NSNotification *)notification{
    
    NSDictionary *zombineDataDict = notification.object;
    if(![zombineDataDict isKindOfClass:[NSDictionary class]] || zombineDataDict.count == 0){
        return;
    }
    MFAPMCrashMonitorModel *zombineModel = [[MFAPMCrashMonitorModel alloc] init];
    zombineModel.zombineClassName = zombineDataDict[@"zombineClassName"] ? : @"";
    zombineModel.zombineFuncName = zombineDataDict[@"zombineFuncName"] ? : @"";
    zombineModel.zombineCallerAddress = zombineDataDict[@"zombineCallerAddress"] ? : @"";
    zombineModel.stack = [MFAPMUtility callerStackStr];
    zombineModel.exception = [NSString stringWithFormat:@"(-[%@ %@]) was sent to a zombie object at address: %@", zombineModel.zombineClassName, zombineModel.zombineFuncName, zombineModel.zombineCallerAddress];
    //扩展信息
    [zombineModel configExtend];
    
    //因程序此时并不崩溃，故保存崩溃信息并等待上传
    [MFAPMUploadManager cacheItem:zombineModel];
}

@end

#pragma mark - Uncaught Exception Actions

static NSUncaughtExceptionHandler *preUncaughtExceptionHandler;

void HandleUncaughtException(NSException *exception){
    
    MFAPMCrashMonitorModel *exceptionModel = [[MFAPMCrashMonitorModel alloc] init];

    //错误信息
    NSString *exceptionDescription = [NSString stringWithFormat:@"errorName:%@\n errorReason:%@\n", [exception name] ? : @"", [exception reason] ? : @""];
    exceptionModel.exception = exceptionDescription;
    
    // 异常的堆栈信息
    NSArray *stackArray = exception.callStackSymbols ? : exception.userInfo[@"callStackSymbols"];
    exceptionModel.stack = [stackArray componentsJoinedByString:@"\n"] ? : @"";
    exceptionModel.isSignal = NO;
    exceptionModel.signal = NSNotFound;
    
    //扩展信息
    [exceptionModel configExtend];
    
    //程序发生崩溃，将数据写入文件，保存崩溃信息
    [MFAPMUploadManager cacheItem:exceptionModel AndStoreToDiskNow:YES];
    
    //如果之前已经注册过一场处理，则将其传出去进行其他模块的异常捕获处理
    if(preUncaughtExceptionHandler){
        preUncaughtExceptionHandler(exception);
    }
}

void InstallUncaughtExceptionHandler(void){
    //保存之前的handler
    preUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
    //进行自定义处理
    NSSetUncaughtExceptionHandler(&HandleUncaughtException);
}

#pragma mark - Signal Exception Actions

typedef void (*SignalHandler)(int signo, siginfo_t *info, void *context);

static SignalHandler preSignalHandler = NULL;

void SignalExceptionHandler(int signal, siginfo_t* info, void* context){
    MFAPMCrashMonitorModel *exceptionModel = [[MFAPMCrashMonitorModel alloc] init];
    exceptionModel.isSignal = YES;
    exceptionModel.signal = signal;
    exceptionModel.stack = [MFAPMUtility callerStackStr];
    [exceptionModel configExtend];
    //程序发生崩溃，将数据写入文件，保存崩溃信息
    [MFAPMUploadManager cacheItem:exceptionModel AndStoreToDiskNow:YES];
    
    // 处理前者注册的 handler
    if (preSignalHandler) {
        preSignalHandler(signal, info, context);
    }
}

void MFAPMSignalRegister(int signal) {
    //设置当前获取到signal后具体处理
    struct sigaction action;
    action.sa_sigaction = SignalExceptionHandler;
    action.sa_flags = SA_NODEFER | SA_SIGINFO;
    sigemptyset(&action.sa_mask);
    sigaction(signal, &action, 0);
}

void InstallSignalExceptionHandler(void){
    
    //保存之前的signal操作
    struct sigaction old_action;
    sigaction(SIGABRT, NULL, &old_action);
    if (old_action.sa_flags & SA_SIGINFO) {
        preSignalHandler = old_action.sa_sigaction;
    }
    
    //注册对应signal的处理方法
    MFAPMSignalRegister(SIGHUP);
    MFAPMSignalRegister(SIGINT);
    MFAPMSignalRegister(SIGQUIT);
    
    MFAPMSignalRegister(SIGABRT);
    MFAPMSignalRegister(SIGILL);
    MFAPMSignalRegister(SIGSEGV);
    MFAPMSignalRegister(SIGFPE);
    MFAPMSignalRegister(SIGBUS);
    MFAPMSignalRegister(SIGPIPE);
}


